/*
 * Copyright (C) 2005 - 2013 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008 - 2013 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010 - 2013 ProjectSkyfire <http://www.projectskyfire.org/>
 *
 * Copyright (C) 2011 - 2013 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "gamePCH.h"
#include "DBCStores.h"

#include "Logging/Log.h"

#include "SharedDefines.h"
#include "SpellMgr.h"

#include "DBCfmt.h"

#include <map>

typedef std::map<uint16, uint32> AreaFlagByAreaID;
typedef std::map<uint32, uint32> AreaFlagByMapID;

struct WMOAreaTableTripple
{
    WMOAreaTableTripple (int32 r, int32 a, int32 g) :
            groupId(g), rootId(r), adtId(a)
    {
    }

    bool operator < (const WMOAreaTableTripple& b) const
    {
        return memcmp(this, &b, sizeof(WMOAreaTableTripple)) < 0;
    }

    // ordered by entropy; that way memcmp will have a minimal medium runtime
    int32 groupId;
    int32 rootId;
    int32 adtId;
};

typedef std::map<WMOAreaTableTripple, WMOAreaTableEntry const *> WMOAreaInfoByTripple;

DBCStorage<AreaTableEntry> sAreaStore(AreaTableEntryfmt);
DBCStorage<AreaGroupEntry> sAreaGroupStore(AreaGroupEntryfmt);
DBCStorage<AreaPOIEntry> sAreaPOIStore(AreaPOIEntryfmt);
static AreaFlagByAreaID sAreaFlagByAreaID;
static AreaFlagByMapID sAreaFlagByMapID;          // for instances without generated *.map files

static WMOAreaInfoByTripple sWMOAreaInfoByTripple;

DBCStorage<AchievementEntry> sAchievementStore(Achievementfmt);
DBCStorage<AchievementCriteriaEntry> sAchievementCriteriaStore(AchievementCriteriafmt);
DBCStorage<AreaTriggerEntry> sAreaTriggerStore(AreaTriggerEntryfmt);
DBCStorage<ArmorLocationEntry> sArmorLocationStore(ArmorLocationfmt);
DBCStorage<AuctionHouseEntry> sAuctionHouseStore(AuctionHouseEntryfmt);
DBCStorage<BankBagSlotPricesEntry> sBankBagSlotPricesStore(BankBagSlotPricesEntryfmt);
DBCStorage<BattlemasterListEntry> sBattlemasterListStore(BattlemasterListEntryfmt);
DBCStorage<BarberShopStyleEntry> sBarberShopStyleStore(BarberShopStyleEntryfmt);
DBCStorage<CharStartOutfitEntry> sCharStartOutfitStore(CharStartOutfitEntryfmt);
DBCStorage<CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt);
DBCStorage<ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt);
DBCStorage<ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt);
DBCStorage<ChrRacesEntry> sChrRacesStore(ChrRacesEntryfmt);
DBCStorage<CinematicSequencesEntry> sCinematicSequencesStore(CinematicSequencesEntryfmt);
DBCStorage<CreatureDisplayInfoEntry> sCreatureDisplayInfoStore(CreatureDisplayInfofmt);
DBCStorage<CreatureFamilyEntry> sCreatureFamilyStore(CreatureFamilyfmt);
DBCStorage<CreatureSpellDataEntry> sCreatureSpellDataStore(CreatureSpellDatafmt);
DBCStorage<CreatureTypeEntry> sCreatureTypeStore(CreatureTypefmt);
DBCStorage<CurrencyTypesEntry> sCurrencyTypesStore(CurrencyTypesfmt);
DBCStorage<DestructibleModelDataEntry> sDestructibleModelDataStore(DestructibleModelDatafmt);

DBCStorage<DungeonEncounterEntry> sDungeonEncounterStore(DungeonEncounterfmt);
DBCStorage<DurabilityQualityEntry> sDurabilityQualityStore(DurabilityQualityfmt);
DBCStorage<DurabilityCostsEntry> sDurabilityCostsStore(DurabilityCostsfmt);

DBCStorage<EmotesEntry> sEmotesStore(EmotesEntryfmt);
DBCStorage<EmotesTextEntry> sEmotesTextStore(EmotesTextEntryfmt);

typedef std::map<uint32, SimpleFactionsList> FactionTeamMap;
static FactionTeamMap sFactionTeamMap;
DBCStorage<FactionEntry> sFactionStore(FactionEntryfmt);
DBCStorage<FactionTemplateEntry> sFactionTemplateStore(FactionTemplateEntryfmt);

DBCStorage<GameObjectDisplayInfoEntry> sGameObjectDisplayInfoStore(GameObjectDisplayInfofmt);
DBCStorage<GemPropertiesEntry> sGemPropertiesStore(GemPropertiesEntryfmt);
DBCStorage<GlyphPropertiesEntry> sGlyphPropertiesStore(GlyphPropertiesfmt);
DBCStorage<GlyphSlotEntry> sGlyphSlotStore(GlyphSlotfmt);

DBCStorage<GtBarberShopCostBaseEntry> sGtBarberShopCostBaseStore(GtBarberShopCostBasefmt);
DBCStorage<GtCombatRatingsEntry> sGtCombatRatingsStore(GtCombatRatingsfmt);
DBCStorage<GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore(GtChanceToMeleeCritBasefmt);
DBCStorage<GtChanceToMeleeCritEntry> sGtChanceToMeleeCritStore(GtChanceToMeleeCritfmt);
DBCStorage<GtChanceToSpellCritBaseEntry> sGtChanceToSpellCritBaseStore(GtChanceToSpellCritBasefmt);
DBCStorage<GtChanceToSpellCritEntry> sGtChanceToSpellCritStore(GtChanceToSpellCritfmt);
//DBCStorage <GtOCTRegenHPEntry>            sGtOCTRegenHPStore(GtOCTRegenHPfmt);
//DBCStorage <GtOCTRegenMPEntry>            sGtOCTRegenMPStore(GtOCTRegenMPfmt);  -- not used currently
//DBCStorage <GtRegenHPPerSptEntry>         sGtRegenHPPerSptStore(GtRegenHPPerSptfmt);
DBCStorage<GtRegenMPPerSptEntry> sGtRegenMPPerSptStore(GtRegenMPPerSptfmt);
DBCStorage<gtSpellScaling> sGtSpellScalingStore(GtSpellScalingfmt);

DBCStorage<HolidaysEntry> sHolidaysStore(Holidaysfmt);

DBCStorage<ItemArmorQualityEntry> sItemArmorQualityStore(ItemArmorQualityfmt);
DBCStorage<ItemArmorShieldEntry> sItemArmorShieldStore(ItemArmorShieldfmt);
DBCStorage<ItemArmorTotalEntry> sItemArmorTotalStore(ItemArmorTotalfmt);
DBCStorage<ItemBagFamilyEntry> sItemBagFamilyStore(ItemBagFamilyfmt);
//DBCStorage <ItemCondExtCostsEntry> sItemCondExtCostsStore(ItemCondExtCostsEntryfmt);
DBCStorage<ItemDamageEntry> sItemDamageAmmoStore(ItemDamagefmt);
DBCStorage<ItemDamageEntry> sItemDamageOneHandStore(ItemDamagefmt);
DBCStorage<ItemDamageEntry> sItemDamageOneHandCasterStore(ItemDamagefmt);
DBCStorage<ItemDamageEntry> sItemDamageRangedStore(ItemDamagefmt);
DBCStorage<ItemDamageEntry> sItemDamageThrownStore(ItemDamagefmt);
DBCStorage<ItemDamageEntry> sItemDamageTwoHandStore(ItemDamagefmt);
DBCStorage<ItemDamageEntry> sItemDamageTwoHandCasterStore(ItemDamagefmt);
DBCStorage<ItemDamageEntry> sItemDamageWandStore(ItemDamagefmt);
//DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore(ItemDisplayTemplateEntryfmt); -- not used currently
DBCStorage<ItemExtendedCostEntry> sItemExtendedCostStore(ItemExtendedCostEntryfmt);
DBCStorage<ItemLimitCategoryEntry> sItemLimitCategoryStore(ItemLimitCategoryEntryfmt);
DBCStorage<ItemRandomPropertiesEntry> sItemRandomPropertiesStore(ItemRandomPropertiesfmt);
DBCStorage<ItemRandomSuffixEntry> sItemRandomSuffixStore(ItemRandomSuffixfmt);
DBCStorage<ItemSetEntry> sItemSetStore(ItemSetEntryfmt);
DBCStorage<ItemReforgeEntry> sItemReforgeStore(ItemReforgefmt);

DBCStorage<LFGDungeonEntry> sLFGDungeonStore(LFGDungeonEntryfmt);

DBCStorage<LockEntry> sLockStore(LockEntryfmt);

DBCStorage<MailTemplateEntry> sMailTemplateStore(MailTemplateEntryfmt);
DBCStorage<MapEntry> sMapStore(MapEntryfmt);

// DBC used only for initialization sMapDifficultyMap at startup.
DBCStorage<MapDifficultyEntry> sMapDifficultyStore(MapDifficultyEntryfmt);          // only for loading
MapDifficultyMap sMapDifficultyMap;

DBCStorage<MovieEntry> sMovieStore(MovieEntryfmt);

DBCStorage<MountCapabilityEntry> sMountCapabilityStore(MountCapabilityfmt);
DBCStorage<MountTypeEntry> sMountTypeStore(MountTypefmt);

DBCStorage<OverrideSpellDataEntry> sOverrideSpellDataStore(OverrideSpellDatafmt);

DBCStorage<PvPDifficultyEntry> sPvPDifficultyStore(PvPDifficultyfmt);

DBCStorage<QuestSortEntry> sQuestSortStore(QuestSortEntryfmt);
DBCStorage<QuestXPEntry> sQuestXPStore(QuestXPfmt);
DBCStorage<QuestFactionRewEntry> sQuestFactionRewardStore(QuestFactionRewardfmt);
DBCStorage<RandomPropertiesPointsEntry> sRandomPropertiesPointsStore(RandomPropertiesPointsfmt);
DBCStorage<ScalingStatDistributionEntry> sScalingStatDistributionStore(ScalingStatDistributionfmt);
DBCStorage<ScalingStatValuesEntry> sScalingStatValuesStore(ScalingStatValuesfmt);

DBCStorage<SkillLineEntry> sSkillLineStore(SkillLinefmt);
DBCStorage<SkillLineAbilityEntry> sSkillLineAbilityStore(SkillLineAbilityfmt);

DBCStorage<SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt);

DBCStorage<SpellItemEnchantmentEntry> sSpellItemEnchantmentStore(SpellItemEnchantmentfmt);
DBCStorage<SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt);
DBCStorage<SpellEntry> sSpellStore(True_SpellEntryfmt);
DBCStorage<SpellEntry_n> sTrueSpellStore(SpellEntryfmt);
SpellCategoryStore sSpellCategoryStore;
PetFamilySpellsStore sPetFamilySpellsStore;

DBCStorage<SpellAuraOptionsEntry> sSpellAuraOptionsStore(SpellAuraOptionsfmt);
DBCStorage<SpellAuraRestrictionsEntry> sSpellAuraRestrictionsStore(SpellAuraRestrictionsfmt);
DBCStorage<SpellCastingRequirementsEntry> sSpellCastingRequirementsStore(SpellCastingRequirementsfmt);
DBCStorage<SpellCategoriesEntry> sSpellCategoriesStore(SpellCategoriesfmt);
DBCStorage<SpellClassOptionsEntry> sSpellClassOptionsStore(SpellClassOptionsfmt);
DBCStorage<SpellCooldownsEntry> sSpellCooldownsStore(SpellCooldownsfmt);
DBCStorage<SpellEffectEntry> sSpellEffectStore(SpellEffectfmt);
DBCStorage<SpellEquippedItemsEntry> sSpellEquippedItemsStore(SpellEquippedItemsfmt);
DBCStorage<SpellInterruptsEntry> sSpellInterruptsStore(SpellInterruptsfmt);
DBCStorage<SpellLevelsEntry> sSpellLevelsStore(SpellLevelsfmt);
DBCStorage<SpellPowerEntry> sSpellPowerStore(SpellPowerfmt);
DBCStorage<SpellReagentsEntry> sSpellReagentsStore(SpellReagentsfmt);
DBCStorage<SpellScalingEntry> sSpellScalingStore(SpellScalingfmt);
DBCStorage<SpellShapeshiftEntry> sSpellShapeshiftStore(SpellShapeshiftfmt);
DBCStorage<SpellTargetRestrictionsEntry> sSpellTargetRestrictionsStore(SpellTargetRestrictionsfmt);
DBCStorage<SpellTotemsEntry> sSpellTotemsStore(SpellTotemsfmt);

SpellEffectMap sSpellEffectMap;

DBCStorage<SpellCastTimesEntry> sSpellCastTimesStore(SpellCastTimefmt);
DBCStorage<SpellDifficultyEntry> sSpellDifficultyStore(SpellDifficultyfmt);
DBCStorage<SpellDurationEntry> sSpellDurationStore(SpellDurationfmt);
DBCStorage<SpellFocusObjectEntry> sSpellFocusObjectStore(SpellFocusObjectfmt);
DBCStorage<SpellRadiusEntry> sSpellRadiusStore(SpellRadiusfmt);
DBCStorage<SpellRangeEntry> sSpellRangeStore(SpellRangefmt);
DBCStorage<SpellRuneCostEntry> sSpellRuneCostStore(SpellRuneCostfmt);
DBCStorage<SpellShapeshiftFormEntry> sSpellShapeshiftFormStore(SpellShapeshiftFormfmt);
//DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore(StableSlotPricesfmt);
DBCStorage<SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt);
DBCStorage<GuildPerksEntry> sGuildPerksStore(GuildPerksfmt);
DBCStorage<TalentEntry> sTalentStore(TalentEntryfmt);
TalentSpellPosMap sTalentSpellPosMap;
DBCStorage<TalentTabEntry> sTalentTabStore(TalentTabEntryfmt);
DBCStorage<TalentTreePrimarySpellsEntry> sTalentTreePrimarySpellsStore(TalentTreePrimarySpellsfmt);

// store absolute bit position for first rank for talent inspect
static uint32 sTalentTabPages[MAX_CLASSES][3];

DBCStorage<TaxiNodesEntry> sTaxiNodesStore(TaxiNodesEntryfmt);
TaxiMask sTaxiNodesMask;
TaxiMask sOldContinentsNodesMask;
TaxiMask sHordeTaxiNodesMask;
TaxiMask sAllianceTaxiNodesMask;
TaxiMask sDeathKnightTaxiNodesMask;

// DBC used only for initialization sTaxiPathSetBySource at startup.
TaxiPathSetBySource sTaxiPathSetBySource;
DBCStorage<TaxiPathEntry> sTaxiPathStore(TaxiPathEntryfmt);

// DBC used only for initialization sTaxiPathNodeStore at startup.
TaxiPathNodesByPath sTaxiPathNodesByPath;
static DBCStorage<TaxiPathNodeEntry> sTaxiPathNodeStore(TaxiPathNodeEntryfmt);

DBCStorage<TotemCategoryEntry> sTotemCategoryStore(TotemCategoryEntryfmt);
DBCStorage<VehicleEntry> sVehicleStore(VehicleEntryfmt);
DBCStorage<VehicleSeatEntry> sVehicleSeatStore(VehicleSeatEntryfmt);
DBCStorage<WMOAreaTableEntry> sWMOAreaTableStore(WMOAreaTableEntryfmt);
DBCStorage<WorldMapAreaEntry> sWorldMapAreaStore(WorldMapAreaEntryfmt);
DBCStorage<WorldMapOverlayEntry> sWorldMapOverlayStore(WorldMapOverlayEntryfmt);
DBCStorage<WorldSafeLocsEntry> sWorldSafeLocsStore(WorldSafeLocsEntryfmt);

typedef std::list<std::string> StoreProblemList;

uint32 DBCFileCount = 0;

static bool LoadDBC_assert_print (uint32 fsize, uint32 rsize, const std::string& filename)
{
    sLog->outError("Size of '%s' setted by format string (%u) not equal size of C++ structure (%u).", filename.c_str(), fsize, rsize);

    // ASSERT must fail after function call
    return false;
}

template<class T>
inline void LoadDBC (uint32& availableDbcLocales, StoreProblemList& errlist, DBCStorage<T>& storage, const std::string& dbc_path, const std::string& filename, const std::string * custom_entries = NULL, const std::string * idname = NULL)
{
    // compatibility format and C++ structure sizes
    if (!(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()) == sizeof(T) || LoadDBC_assert_print(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()), sizeof(T), filename)))
        return;

    ++DBCFileCount;
    std::string dbc_filename = dbc_path + filename;
    SqlDbc * sql = NULL;
    if (custom_entries)
        sql = new SqlDbc(&filename, custom_entries, idname, storage.GetFormat());

    if (storage.Load(dbc_filename.c_str(), sql))
    {
    }
    else
    {
        // sort problematic dbc to (1) non compatible and (2) non-existed
        FILE * f = fopen(dbc_filename.c_str(), "rb");
        if (f)
        {
            printf("Can't LOAD dbc %s !\n", dbc_filename.c_str());
            char buf[100];
            snprintf(buf, 100, " (exist, but have %d fields instead " SIZEFMTD ") Wrong client version DBC file?", storage.GetFieldCount(), strlen(storage.GetFormat()));
            errlist.push_back(dbc_filename + buf);
            fclose(f);
        }
        else
        {
            printf("Can't OPEN dbc %s !\n", dbc_filename.c_str());
            errlist.push_back(dbc_filename);
        }
    }

    delete sql;
}

void LoadDBCStores (const std::string& dataPath)
{
    uint32 oldMSTime = getMSTime();

    std::string dbcPath = dataPath + "dbc/";

    StoreProblemList bad_dbc_files;
    uint32 availableDbcLocales = 0xFFFFFFFF;

    LoadDBC(availableDbcLocales, bad_dbc_files, sAreaStore, dbcPath, "AreaTable.dbc");

    // must be after sAreaStore loading
    for (uint32 i = 0; i < sAreaStore.GetNumRows(); ++i)          // areaflag numbered from 0
    {
        if (AreaTableEntry const* area = sAreaStore.LookupEntry(i))
        {
            // fill AreaId->DBC records
            sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID), area->exploreFlag));

            // fill MapId->DBC records (skip sub zones and continents)
            if (area->zone == 0 && area->mapid != 0 && area->mapid != 1 && area->mapid != 530 && area->mapid != 571)
                sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid, area->exploreFlag));
        }
    }

    LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementStore, dbcPath, "Achievement.dbc"/*, &CustomAchievementfmt, &CustomAchievementIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementCriteriaStore, dbcPath, "Achievement_Criteria.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sAreaTriggerStore, dbcPath, "AreaTrigger.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sArmorLocationStore, dbcPath, "ArmorLocation.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sAreaGroupStore, dbcPath, "AreaGroup.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sAreaPOIStore, dbcPath, "AreaPOI.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sAuctionHouseStore, dbcPath, "AuctionHouse.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sBankBagSlotPricesStore, dbcPath, "BankBagSlotPrices.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sBattlemasterListStore, dbcPath, "BattlemasterList.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sBarberShopStyleStore, dbcPath, "BarberShopStyle.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sCharStartOutfitStore, dbcPath, "CharStartOutfit.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sCharTitlesStore, dbcPath, "CharTitles.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sChatChannelsStore, dbcPath, "ChatChannels.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sChrClassesStore, dbcPath, "ChrClasses.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sChrRacesStore, dbcPath, "ChrRaces.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicSequencesStore, dbcPath, "CinematicSequences.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoStore, dbcPath, "CreatureDisplayInfo.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureFamilyStore, dbcPath, "CreatureFamily.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureSpellDataStore, dbcPath, "CreatureSpellData.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureTypeStore, dbcPath, "CreatureType.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sCurrencyTypesStore, dbcPath, "CurrencyTypes.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sDungeonEncounterStore, dbcPath, "DungeonEncounter.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sDurabilityCostsStore, dbcPath, "DurabilityCosts.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sDurabilityQualityStore, dbcPath, "DurabilityQuality.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sEmotesStore, dbcPath, "Emotes.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sEmotesTextStore, dbcPath, "EmotesText.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sFactionStore, dbcPath, "Faction.dbc");
    for (uint32 i = 0; i < sFactionStore.GetNumRows(); ++i)
    {
        FactionEntry const * faction = sFactionStore.LookupEntry(i);
        if (faction && faction->team)
        {
            SimpleFactionsList &flist = sFactionTeamMap[faction->team];
            flist.push_back(i);
        }
    }

    LoadDBC(availableDbcLocales, bad_dbc_files, sFactionTemplateStore, dbcPath, "FactionTemplate.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sGameObjectDisplayInfoStore, dbcPath, "GameObjectDisplayInfo.dbc");
    for (uint32 i = 0; i < sGameObjectDisplayInfoStore.GetNumRows(); ++i)
    {
        if (GameObjectDisplayInfoEntry const * info = sGameObjectDisplayInfoStore.LookupEntry(i))
        {
            if (info->maxX < info->minX)
                std::swap(*(float*) (&info->maxX), *(float*) (&info->minX));
            if (info->maxY < info->minY)
                std::swap(*(float*) (&info->maxY), *(float*) (&info->minY));
            if (info->maxZ < info->minZ)
                std::swap(*(float*) (&info->maxZ), *(float*) (&info->minZ));
        }
    }

    LoadDBC(availableDbcLocales, bad_dbc_files, sGemPropertiesStore, dbcPath, "GemProperties.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sGlyphPropertiesStore, dbcPath, "GlyphProperties.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sGlyphSlotStore, dbcPath, "GlyphSlot.dbc");

    LoadDBC(availableDbcLocales, bad_dbc_files, sGtBarberShopCostBaseStore, dbcPath, "gtBarberShopCostBase.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sGtCombatRatingsStore, dbcPath, "gtCombatRatings.dbc");

    LoadDBC(availableDbcLocales, bad_dbc_files, sGtChanceToMeleeCritBaseStore, dbcPath, "gtChanceToMeleeCritBase.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sGtChanceToMeleeCritStore, dbcPath, "gtChanceToMeleeCrit.dbc");

    LoadDBC(availableDbcLocales, bad_dbc_files, sGtChanceToSpellCritBaseStore, dbcPath, "gtChanceToSpellCritBase.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sGtChanceToSpellCritStore, dbcPath, "gtChanceToSpellCrit.dbc");
    //LoadDBC(availableDbcLocales, bad_dbc_files, sGtOCTRegenHPStore,        dbcPath, "gtOCTRegenHP.dbc");
    //LoadDBC(availableDbcLocales, bad_dbc_files, sGtOCTRegenMPStore,        dbcPath, "gtOCTRegenMP.dbc");       -- not used currently
    //LoadDBC(availableDbcLocales, bad_dbc_files, sGtRegenHPPerSptStore,     dbcPath, "gtRegenHPPerSpt.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sGtRegenMPPerSptStore, dbcPath, "gtRegenMPPerSpt.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sGtSpellScalingStore, dbcPath, "gtSpellScaling.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sHolidaysStore, dbcPath, "Holidays.dbc");

    //LoadDBC(availableDbcLocales, bad_dbc_files, sItemStore,                dbcPath, "Item.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemBagFamilyStore, dbcPath, "ItemBagFamily.dbc");
    //LoadDBC(availableDbcLocales, bad_dbc_files, sItemDisplayInfoStore,     dbcPath, "ItemDisplayInfo.dbc");     -- not used currently
    //LoadDBC(availableDbcLocales, bad_dbc_files, sItemCondExtCostsStore,    dbcPath, "ItemCondExtCosts.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemExtendedCostStore, dbcPath, "ItemExtendedCost.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemLimitCategoryStore, dbcPath, "ItemLimitCategory.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemRandomPropertiesStore, dbcPath, "ItemRandomProperties.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemRandomSuffixStore, dbcPath, "ItemRandomSuffix.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemSetStore, dbcPath, "ItemSet.dbc");

    LoadDBC(availableDbcLocales, bad_dbc_files, sItemArmorQualityStore, dbcPath, "ItemArmorQuality.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemArmorShieldStore, dbcPath, "ItemArmorShield.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemArmorTotalStore, dbcPath, "ItemArmorTotal.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemDamageAmmoStore, dbcPath, "ItemDamageAmmo.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemDamageOneHandStore, dbcPath, "ItemDamageOneHand.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemDamageOneHandCasterStore, dbcPath, "ItemDamageOneHandCaster.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemDamageRangedStore, dbcPath, "ItemDamageRanged.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemDamageThrownStore, dbcPath, "ItemDamageThrown.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemDamageTwoHandStore, dbcPath, "ItemDamageTwoHand.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemDamageTwoHandCasterStore, dbcPath, "ItemDamageTwoHandCaster.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemDamageWandStore, dbcPath, "ItemDamageWand.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sItemReforgeStore, dbcPath, "ItemReforge.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sLFGDungeonStore, dbcPath, "LFGDungeons.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sLockStore, dbcPath, "Lock.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sMailTemplateStore, dbcPath, "MailTemplate.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sMapStore, dbcPath, "Map.dbc");

    LoadDBC(availableDbcLocales, bad_dbc_files, sMapDifficultyStore, dbcPath, "MapDifficulty.dbc");
    // fill data
    for (uint32 i = 1; i < sMapDifficultyStore.GetNumRows(); ++i)
        if (MapDifficultyEntry const* entry = sMapDifficultyStore.LookupEntry(i))
            sMapDifficultyMap[MAKE_PAIR32(entry->MapId, entry->Difficulty)] = MapDifficulty(entry->resetTime, entry->maxPlayers, strlen(entry->areaTriggerText) > 0);
    sMapDifficultyStore.Clear();

    LoadDBC(availableDbcLocales, bad_dbc_files, sMovieStore, dbcPath, "Movie.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sMountCapabilityStore, dbcPath, "MountCapability.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sMountTypeStore, dbcPath, "MountType.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sOverrideSpellDataStore, dbcPath, "OverrideSpellData.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sQuestSortStore, dbcPath, "QuestSort.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sPvPDifficultyStore, dbcPath, "PvpDifficulty.dbc");
    for (uint32 i = 0; i < sPvPDifficultyStore.GetNumRows(); ++i)
        if (PvPDifficultyEntry const* entry = sPvPDifficultyStore.LookupEntry(i))
            if (entry->bracketId > MAX_BATTLEGROUND_BRACKETS)
                ASSERT(false && "Need update MAX_BATTLEGROUND_BRACKETS by DBC data");

    LoadDBC(availableDbcLocales, bad_dbc_files, sQuestXPStore, dbcPath, "QuestXP.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sQuestFactionRewardStore, dbcPath, "QuestFactionReward.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sRandomPropertiesPointsStore, dbcPath, "RandPropPoints.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sScalingStatDistributionStore, dbcPath, "ScalingStatDistribution.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sScalingStatValuesStore, dbcPath, "ScalingStatValues.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSkillLineStore, dbcPath, "SkillLine.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSkillLineAbilityStore, dbcPath, "SkillLineAbility.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSoundEntriesStore, dbcPath, "SoundEntries.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellAuraOptionsStore, dbcPath, "SpellAuraOptions.dbc"/*, &CustomSpellAuraOptionsEntryfmt, &CustomSpellAuraOptionsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellAuraRestrictionsStore, dbcPath, "SpellAuraRestrictions.dbc"/*, &CustomSpellAuraRestrictionsEntryfmt, &CustomSpellAuraRestrictionsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCastingRequirementsStore, dbcPath, "SpellCastingRequirements.dbc"/*, &CustomSpellCastingRequirementsEntryfmt, &CustomSpellCastingRequirementsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCategoriesStore, dbcPath, "SpellCategories.dbc"/*, &CustomSpellCategoriesEntryfmt, &CustomSpellCategoriesEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellClassOptionsStore, dbcPath, "SpellClassOptions.dbc"/*, &CustomSpellClassOptionsEntryfmt, &CustomSpellClassOptionsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCooldownsStore, dbcPath, "SpellCooldowns.dbc"/*, &CustomSpellCooldownsEntryfmt, &CustomSpellCooldownsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellEffectStore, dbcPath, "SpellEffect.dbc"/*, &CustomSpellEffectEntryfmt, &CustomSpellEffectEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellEquippedItemsStore, dbcPath, "SpellEquippedItems.dbc"/*, &CustomSpellEquippedItemsEntryfmt, &CustomSpellEquippedItemsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellInterruptsStore, dbcPath, "SpellInterrupts.dbc"/*, &CustomSpellInterruptsEntryfmt, &CustomSpellInterruptsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellLevelsStore, dbcPath, "SpellLevels.dbc"/*, &CustomSpellLevelsEntryfmt, &CustomSpellLevelsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellPowerStore, dbcPath, "SpellPower.dbc"/*, &CustomSpellPowerEntryfmt, &CustomSpellPowerEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellReagentsStore, dbcPath, "SpellReagents.dbc"/*, &CustomSpellReagentsEntryfmt, &CustomSpellReagentsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellScalingStore, dbcPath, "SpellScaling.dbc"/*, &CustomSpellScalingEntryfmt, &CustomSpellScalingEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellShapeshiftStore, dbcPath, "SpellShapeshift.dbc"/*, &CustomSpellShapeshiftEntryfmt, &CustomSpellShapeshiftEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellTargetRestrictionsStore, dbcPath, "SpellTargetRestrictions.dbc"/*, &CustomSpellTargetRestrictionsEntryfmt, &CustomSpellTargetRestrictionsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellTotemsStore, dbcPath, "SpellTotems.dbc"/*, &CustomSpellTotemsEntryfmt, &CustomSpellTotemsEntryIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sTrueSpellStore, dbcPath, "Spell.dbc"/*, &CustomSpellEntryfmt, &CustomSpellEntryIndex*/);

    for (uint32 i = 1; i < sSpellEffectStore.GetNumRows(); ++i)
    {
        if (SpellEffectEntry const *spellEffect = sSpellEffectStore.LookupEntry(i))
            sSpellEffectMap[spellEffect->EffectSpellId].effects[spellEffect->EffectIndex] = spellEffect;
    }

    sSpellStore.Clear();
    sSpellStore.nCount = sTrueSpellStore.nCount;
    sSpellStore.fieldCount = strlen(sSpellStore.fmt);
    sSpellStore.indexTable = new SpellEntry*[sSpellStore.nCount];
    for (uint32 i = 0; i < sTrueSpellStore.GetNumRows(); ++i)
    {
        SpellEntry_n* spell = sTrueSpellStore.LookupEntryNoConst(i);
        if (spell)
        {
            SpellEntry *newspell = new SpellEntry(spell);
            sSpellStore.SetEntry(i, newspell);

            if (newspell->Category)
                sSpellCategoryStore[newspell->Category].insert(i);
        }
        else
        {
            sSpellStore.indexTable[i] = NULL;
        }
    }

    for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
    {
        SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j);

        if (!skillLine)
            continue;

        SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId);

        if (spellInfo && IsPassiveSpell(spellInfo->Id))
        {
            for (uint32 i = 1; i < sCreatureFamilyStore.GetNumRows(); ++i)
            {
                CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(i);
                if (!cFamily)
                    continue;

                if (skillLine->skillId != cFamily->skillLine[0] && skillLine->skillId != cFamily->skillLine[1])
                    continue;
                if (spellInfo->spellLevel)
                    continue;

                if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL)
                    continue;

                sPetFamilySpellsStore[i].insert(spellInfo->Id);
            }
        }
    }

    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCastTimesStore, dbcPath, "SpellCastTimes.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellDifficultyStore, dbcPath, "SpellDifficulty.dbc"/*, &CustomSpellDifficultyfmt, &CustomSpellDifficultyIndex*/);
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellDurationStore, dbcPath, "SpellDuration.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellFocusObjectStore, dbcPath, "SpellFocusObject.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellItemEnchantmentStore, dbcPath, "SpellItemEnchantment.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellItemEnchantmentConditionStore, dbcPath, "SpellItemEnchantmentCondition.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellRadiusStore, dbcPath, "SpellRadius.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellRangeStore, dbcPath, "SpellRange.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellRuneCostStore, dbcPath, "SpellRuneCost.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSpellShapeshiftFormStore, dbcPath, "SpellShapeshiftForm.dbc");
    //LoadDBC(availableDbcLocales, bad_dbc_files, sStableSlotPricesStore,    dbcPath, "StableSlotPrices.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sSummonPropertiesStore, dbcPath, "SummonProperties.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sTalentStore, dbcPath, "Talent.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sGuildPerksStore, dbcPath, "GuildPerkSpells.dbc");

    // Create Spelldifficulty searcher
    for (uint32 i = 0; i < sSpellDifficultyStore.GetNumRows(); ++i)
    {
        SpellDifficultyEntry const *spellDiff = sSpellDifficultyStore.LookupEntry(i);
        if (!spellDiff)
            continue;

        SpellDifficultyEntry newEntry;
        memset(newEntry.SpellID, 0, 4 * sizeof(uint32));
        for (int x = 0; x < MAX_DIFFICULTY; ++x)
        {
            if (spellDiff->SpellID[x] <= 0 || !sSpellStore.LookupEntry(spellDiff->SpellID[x]))
            {
                if (spellDiff->SpellID[x] > 0)          //don't show error if spell is <= 0, not all modes have spells and there are unknown negative values
                    sLog->outDebug(LOG_FILTER_NETWORKIO, "spelldifficulty_dbc: spell %i at field id:%u at spellid%i does not exist in SpellStore (spell.dbc), loaded as 0", spellDiff->SpellID[x], spellDiff->ID, x);
                newEntry.SpellID[x] = 0;          //spell was <= 0 or invalid, set to 0
            }
            else
                newEntry.SpellID[x] = spellDiff->SpellID[x];
        }
        if (newEntry.SpellID[0] <= 0 || newEntry.SpellID[1] <= 0)          //id0-1 must be always set!
            continue;

        for (int x = 0; x < MAX_DIFFICULTY; ++x)
            sSpellMgr->SetSpellDifficultyId(uint32(newEntry.SpellID[x]), spellDiff->ID);
    }

    // create talent spells set
    for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i)
    {
        TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
        if (!talentInfo)
            continue;
        for (int j = 0; j < MAX_TALENT_RANK; j++)
            if (talentInfo->RankID[j])
                sTalentSpellPosMap[talentInfo->RankID[j]] = TalentSpellPos(i, j);
    }

    LoadDBC(availableDbcLocales, bad_dbc_files, sTalentTabStore, dbcPath, "TalentTab.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sTalentTreePrimarySpellsStore, dbcPath, "TalentTreePrimarySpells.dbc");

    // prepare fast data access to bit pos of talent ranks for use at inspecting
    {
        // now have all max ranks (and then bit amount used for store talent ranks in inspect)
        for (uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId)
        {
            TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentTabId);
            if (!talentTabInfo)
                continue;

            // prevent memory corruption; otherwise cls will become 12 below
            if ((talentTabInfo->ClassMask & CLASSMASK_ALL_PLAYABLE) == 0)
                continue;

            // store class talent tab pages
            uint32 cls = 1;
            for (uint32 m = 1; !(m & talentTabInfo->ClassMask) && cls < MAX_CLASSES; m <<= 1, ++cls) {}

            sTalentTabPages[cls][talentTabInfo->tabpage] = talentTabId;
        }
    }

    LoadDBC(availableDbcLocales, bad_dbc_files, sTaxiNodesStore, dbcPath, "TaxiNodes.dbc");

    LoadDBC(availableDbcLocales, bad_dbc_files, sTaxiPathStore, dbcPath, "TaxiPath.dbc");
    for (uint32 i = 1; i < sTaxiPathStore.GetNumRows(); ++i)
        if (TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(i))
            sTaxiPathSetBySource[entry->from][entry->to] = TaxiPathBySourceAndDestination(entry->ID, entry->price);
    uint32 pathCount = sTaxiPathStore.GetNumRows();

    //## TaxiPathNode.dbc ## Loaded only for initialization different structures
    LoadDBC(availableDbcLocales, bad_dbc_files, sTaxiPathNodeStore, dbcPath, "TaxiPathNode.dbc");
    // Calculate path nodes count
    std::vector<uint32> pathLength;
    pathLength.resize(pathCount);          // 0 and some other indexes not used
    for (uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i)
        if (TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i))
        {
            if (pathLength[entry->path] < entry->index + 1)
                pathLength[entry->path] = entry->index + 1;
        }
    // Set path length
    sTaxiPathNodesByPath.resize(pathCount);          // 0 and some other indexes not used
    for (uint32 i = 1; i < sTaxiPathNodesByPath.size(); ++i)
        sTaxiPathNodesByPath[i].resize(pathLength[i]);
    // fill data
    for (uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i)
        if (TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i))
            sTaxiPathNodesByPath[entry->path].set(entry->index, entry);

    // Initialize global taxinodes mask
    // include existed nodes that have at least single not spell base (scripted) path
    {
        std::set<uint32> spellPaths;
        for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
            if (SpellEntry const* sInfo = sSpellStore.LookupEntry(i))
                for (int j = 0; j < MAX_SPELL_EFFECTS; ++j)
                    if (sInfo->Effect[j] == SPELL_EFFECT_SEND_TAXI)
                        spellPaths.insert(sInfo->EffectMiscValue[j]);

        ASSERT(((sTaxiNodesStore.GetNumRows()-1)/32) < TaxiMaskSize && "TaxiMaskSize needs to be increased");
        memset(sTaxiNodesMask, 0, sizeof(sTaxiNodesMask));
        memset(sOldContinentsNodesMask, 0, sizeof(sOldContinentsNodesMask));
        memset(sHordeTaxiNodesMask, 0, sizeof(sHordeTaxiNodesMask));
        memset(sAllianceTaxiNodesMask, 0, sizeof(sAllianceTaxiNodesMask));
        memset(sDeathKnightTaxiNodesMask, 0, sizeof(sDeathKnightTaxiNodesMask));
        for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
        {
            TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
            if (!node)
                continue;

            TaxiPathSetBySource::const_iterator src_i = sTaxiPathSetBySource.find(i);
            if (src_i != sTaxiPathSetBySource.end() && !src_i->second.empty())
            {
                bool ok = false;
                for (TaxiPathSetForSource::const_iterator dest_i = src_i->second.begin(); dest_i != src_i->second.end(); ++dest_i)
                {
                    // not spell path
                    if (spellPaths.find(dest_i->second.ID) == spellPaths.end())
                    {
                        ok = true;
                        break;
                    }
                }

                if (!ok)
                    continue;
            }

            // valid taxi network node
            uint8 field = (uint8) ((i - 1) / 32);
            uint32 submask = 1 << ((i - 1) % 32);
            sTaxiNodesMask[field] |= submask;

            if (node->MountCreatureID[0] && node->MountCreatureID[0] != 32981)
                sHordeTaxiNodesMask[field] |= submask;
            if (node->MountCreatureID[1] && node->MountCreatureID[1] != 32981)
                sAllianceTaxiNodesMask[field] |= submask;
            if (node->MountCreatureID[0] == 32981 || node->MountCreatureID[1] == 32981)
                sDeathKnightTaxiNodesMask[field] |= submask;

            // old continent node (+ nodes virtually at old continents, check explicitly to avoid loading map files for zone info)
            if (node->map_id < 2 || i == 82 || i == 83 || i == 93 || i == 94)
                sOldContinentsNodesMask[field] |= submask;

            // fix DK node at Ebon Hold
            if (i == 315)
            {
                ((TaxiNodesEntry*) node)->MountCreatureID[1] = 32981;
            }
        }
    }

    LoadDBC(availableDbcLocales, bad_dbc_files, sTotemCategoryStore, dbcPath, "TotemCategory.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sVehicleStore, dbcPath, "Vehicle.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sVehicleSeatStore, dbcPath, "VehicleSeat.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sWMOAreaTableStore, dbcPath, "WMOAreaTable.dbc");
    for (uint32 i = 0; i < sWMOAreaTableStore.GetNumRows(); ++i)
    {
        if (WMOAreaTableEntry const* entry = sWMOAreaTableStore.LookupEntry(i))
        {
            sWMOAreaInfoByTripple.insert(WMOAreaInfoByTripple::value_type(WMOAreaTableTripple(entry->rootId, entry->adtId, entry->groupId), entry));
        }
    }
    LoadDBC(availableDbcLocales, bad_dbc_files, sWorldMapAreaStore, dbcPath, "WorldMapArea.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sWorldMapOverlayStore, dbcPath, "WorldMapOverlay.dbc");
    LoadDBC(availableDbcLocales, bad_dbc_files, sWorldSafeLocsStore, dbcPath, "WorldSafeLocs.dbc");

    // error checks
    if (bad_dbc_files.size() >= DBCFileCount)
    {
        sLog->outError("\nIncorrect DataDir value in worldserver.conf or ALL required *.dbc files (%d) not found by path: %sdbc", DBCFileCount, dataPath.c_str());
        exit(1);
    }
    else if (!bad_dbc_files.empty())
    {
        std::string str;
        for (std::list<std::string>::iterator i = bad_dbc_files.begin(); i != bad_dbc_files.end(); ++i)
            str += *i + "\n";

        sLog->outError("\nSome required *.dbc files (%u from %d) not found or not compatible:\n%s", (uint32) bad_dbc_files.size(), DBCFileCount, str.c_str());
        exit(1);
    }

    // Check loaded DBC files proper version
    if (!sAreaStore.LookupEntry(4445) ||          // last area (areaflag) added in 4.0.6a
    !sCharTitlesStore.LookupEntry(229) ||          // last char title added in 4.0.6a
    !sGemPropertiesStore.LookupEntry(1858) ||          // last gem property added in 4.0.6a
    !sItemExtendedCostStore.LookupEntry(3400) ||          // last item extended cost added in 4.0.6a
    !sMapStore.LookupEntry(767) ||          // last map added in 4.0.6a
    !sSpellStore.LookupEntry(96539))          // last added spell in 4.0.6a

    {
        sLog->outError("\nYou have _outdated_ DBC files. Please extract correct versions from current using client.");
        exit(1);
    }

    sLog->outString(">> Initialized %d data stores in %u ms", DBCFileCount, GetMSTimeDiffToNow(oldMSTime));
    sLog->outString();
}

SimpleFactionsList const* GetFactionTeamList (uint32 faction)
{
    FactionTeamMap::const_iterator itr = sFactionTeamMap.find(faction);
    if (itr != sFactionTeamMap.end())
        return &itr->second;

    return NULL;
}

const char* GetPetName (uint32 petfamily)
{
    if (!petfamily)
        return NULL;
    CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(petfamily);
    if (!pet_family)
        return NULL;
    return pet_family->Name ? pet_family->Name : NULL;
}

TalentSpellPos const* GetTalentSpellPos (uint32 spellId)
{
    TalentSpellPosMap::const_iterator itr = sTalentSpellPosMap.find(spellId);
    if (itr == sTalentSpellPosMap.end())
        return NULL;

    return &itr->second;
}

SpellEffectEntry const* GetSpellEffectEntry (uint32 spellId, uint32 effect)
{
    SpellEffectMap::const_iterator itr = sSpellEffectMap.find(spellId);
    if (itr == sSpellEffectMap.end())
        return NULL;

    return itr->second.effects[effect];
}

uint32 GetTalentSpellCost (uint32 spellId)
{
    if (TalentSpellPos const* pos = GetTalentSpellPos(spellId))
        return pos->rank + 1;

    return 0;
}

int32 GetAreaFlagByAreaID (uint32 area_id)
{
    AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id);
    if (i == sAreaFlagByAreaID.end())
        return -1;

    return i->second;
}

WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple (int32 rootid, int32 adtid, int32 groupid)
{
    WMOAreaInfoByTripple::iterator i = sWMOAreaInfoByTripple.find(WMOAreaTableTripple(rootid, adtid, groupid));
    if (i == sWMOAreaInfoByTripple.end())
        return NULL;
    return i->second;
}

AreaTableEntry const* GetAreaEntryByAreaID (uint32 area_id)
{
    int32 areaflag = GetAreaFlagByAreaID(area_id);
    if (areaflag < 0)
        return NULL;

    return sAreaStore.LookupEntry(areaflag);
}

AreaTableEntry const* GetAreaEntryByAreaFlagAndMap (uint32 area_flag, uint32 map_id)
{
    if (area_flag)
        return sAreaStore.LookupEntry(area_flag);

    if (MapEntry const* mapEntry = sMapStore.LookupEntry(map_id))
        return GetAreaEntryByAreaID(mapEntry->linked_zone);

    return NULL;
}

uint32 GetAreaFlagByMapId (uint32 mapid)
{
    AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid);
    if (i == sAreaFlagByMapID.end())
        return 0;
    else
        return i->second;
}

uint32 GetVirtualMapForMapAndZone (uint32 mapid, uint32 zoneId)
{
    if (mapid != 530 && mapid != 571)          // speed for most cases
        return mapid;

    if (WorldMapAreaEntry const* wma = sWorldMapAreaStore.LookupEntry(zoneId))
        return wma->virtual_map_id >= 0 ? wma->virtual_map_id : wma->map_id;

    return mapid;
}

ContentLevels GetContentLevelsForMapAndZone (uint32 mapid, uint32 zoneId)
{
    mapid = GetVirtualMapForMapAndZone(mapid, zoneId);
    if (mapid < 2 || mapid == 648 || mapid == 654 || mapid == 638 || mapid == 655 || mapid == 656 || mapid == 661 || mapid == 659)
        return CONTENT_1_60;

    if (zoneId == 5034 || zoneId == 4922 || zoneId == 616 || zoneId == 5146 || zoneId == 5042)
        return CONTENT_81_85;

    MapEntry const* mapEntry = sMapStore.LookupEntry(mapid);
    if (!mapEntry)
        return CONTENT_1_60;

    switch (mapEntry->Expansion())
    {
    case 1:
        return CONTENT_61_70;
    case 2:
        return CONTENT_71_80;
    case 3:
        return CONTENT_81_85;
    default:
        return CONTENT_1_60;
    }
}

bool IsTotemCategoryCompatiableWith (uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId)
{
    if (requiredTotemCategoryId == 0)
        return true;
    if (itemTotemCategoryId == 0)
        return false;

    TotemCategoryEntry const* itemEntry = sTotemCategoryStore.LookupEntry(itemTotemCategoryId);
    if (!itemEntry)
        return false;
    TotemCategoryEntry const* reqEntry = sTotemCategoryStore.LookupEntry(requiredTotemCategoryId);
    if (!reqEntry)
        return false;

    if (itemEntry->categoryType != reqEntry->categoryType)
        return false;

    return (itemEntry->categoryMask & reqEntry->categoryMask) == reqEntry->categoryMask;
}

void Zone2MapCoordinates (float& x, float& y, uint32 zone)
{
    WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone);

    // if not listed then map coordinates (instance)
    if (!maEntry)
        return;

    std::swap(x, y);          // at client map coords swapped
    x = x * ((maEntry->x2 - maEntry->x1) / 100) + maEntry->x1;
    y = y * ((maEntry->y2 - maEntry->y1) / 100) + maEntry->y1;          // client y coord from top to down
}

void Map2ZoneCoordinates (float& x, float& y, uint32 zone)
{
    WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone);

    // if not listed then map coordinates (instance)
    if (!maEntry)
        return;

    x = (x - maEntry->x1) / ((maEntry->x2 - maEntry->x1) / 100);
    y = (y - maEntry->y1) / ((maEntry->y2 - maEntry->y1) / 100);          // client y coord from top to down
    std::swap(x, y);          // client have map coords swapped
}

MapDifficulty const* GetMapDifficultyData (uint32 mapId, Difficulty difficulty)
{
    MapDifficultyMap::const_iterator itr = sMapDifficultyMap.find(MAKE_PAIR32(mapId, difficulty));
    return itr != sMapDifficultyMap.end() ? &itr->second : NULL;
}

MapDifficulty const* GetDownscaledMapDifficultyData (uint32 mapId, Difficulty &difficulty)
{
    uint32 tmpDiff = difficulty;
    MapDifficulty const* mapDiff = GetMapDifficultyData(mapId, Difficulty(tmpDiff));
    if (!mapDiff)
    {
        if (tmpDiff > RAID_DIFFICULTY_25MAN_NORMAL)          // heroic, downscale to normal
            tmpDiff -= 2;
        else
            tmpDiff -= 1;          // any non-normal mode for raids like tbc (only one mode)

        // pull new data
        mapDiff = GetMapDifficultyData(mapId, Difficulty(tmpDiff));          // we are 10 normal or 25 normal
        if (!mapDiff)
        {
            tmpDiff -= 1;
            mapDiff = GetMapDifficultyData(mapId, Difficulty(tmpDiff));          // 10 normal
        }
    }

    difficulty = Difficulty(tmpDiff);
    return mapDiff;
}

PvPDifficultyEntry const* GetBattlegroundBracketByLevel (uint32 mapid, uint32 level)
{
    PvPDifficultyEntry const* maxEntry = NULL;          // used for level > max listed level case
    for (uint32 i = 0; i < sPvPDifficultyStore.GetNumRows(); ++i)
    {
        if (PvPDifficultyEntry const* entry = sPvPDifficultyStore.LookupEntry(i))
        {
            // skip unrelated and too-high brackets
            if (entry->mapId != mapid || entry->minLevel > level)
                continue;

            // exactly fit
            if (entry->maxLevel >= level)
                return entry;

            // remember for possible out-of-range case (search higher from existed)
            if (!maxEntry || maxEntry->maxLevel < entry->maxLevel)
                maxEntry = entry;
        }
    }

    return maxEntry;
}

PvPDifficultyEntry const* GetBattlegroundBracketById (uint32 mapid, BattlegroundBracketId id)
{
    for (uint32 i = 0; i < sPvPDifficultyStore.GetNumRows(); ++i)
        if (PvPDifficultyEntry const* entry = sPvPDifficultyStore.LookupEntry(i))
            if (entry->mapId == mapid && entry->GetBracketId() == id)
                return entry;

    return NULL;
}

uint32 const* GetTalentTabPages (uint8 cls)
{
    return sTalentTabPages[cls];
}

float GetGtSpellScalingValue (int8 class_, uint8 level)
{
    if (class_ == -1)
        class_ = MAX_CLASSES;          // General distribution
    if (class_ == 0)
        return -1.0f;          // shouldn't scale

    //They really wants that players reach level 100... in the 5th expansion.
    const gtSpellScaling * spellscaling = sGtSpellScalingStore.LookupEntry((class_ - 1) * 100 + level - 1);
    if (spellscaling)
        return spellscaling->coef;
    else
        return -1.0f;
}

// script support functions
DBCStorage<SoundEntriesEntry> const* GetSoundEntriesStore ()
{
    return &sSoundEntriesStore;
}
DBCStorage<SpellEntry> const* GetSpellStore ()
{
    return &sSpellStore;
}
DBCStorage<SpellRangeEntry> const* GetSpellRangeStore ()
{
    return &sSpellRangeStore;
}
DBCStorage<FactionEntry> const* GetFactionStore ()
{
    return &sFactionStore;
}
DBCStorage<CreatureDisplayInfoEntry> const* GetCreatureDisplayStore ()
{
    return &sCreatureDisplayInfoStore;
}
DBCStorage<EmotesEntry> const* GetEmotesStore ()
{
    return &sEmotesStore;
}
DBCStorage<EmotesTextEntry> const* GetEmotesTextStore ()
{
    return &sEmotesTextStore;
}
DBCStorage<AchievementEntry> const* GetAchievementStore ()
{
    return &sAchievementStore;
}
